package dashboard.common.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * {@linkplain org.springframework.beans.factory.config.PropertyPlaceholderConfigurer}
 * ，增加默认值的功能。 例如：${placeholder:defaultValue}，假如placeholder的值不存在，则默认取得
 * defaultValue。
 * @version 1.0.0
 */
@Slf4j
public class PropertyPlaceholderConfigurer extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer implements ResourceLoaderAware, InitializingBean {

    private static final String PLACEHOLDER_PREFIX = "${";

    private static final String PLACEHOLDER_SUFFIX = "}";

    // 存取properties配置文件key-value结果
    private Properties props;

    private ResourceLoader loader;

    private String[]            locationNames;

    public PropertyPlaceholderConfigurer(){
        setIgnoreUnresolvablePlaceholders(true);
    }

    public void setResourceLoader(ResourceLoader loader) {
        this.loader = loader;
    }

    public void setLocationNames(String[] locations) {
        this.locationNames = locations;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(loader, "no resourceLoader");

        if (locationNames != null) {
            for (int i = 0; i < locationNames.length; i++) {
                locationNames[i] = resolveSystemPropertyPlaceholders(locationNames[i]);
            }
        }

        if (locationNames != null) {
            List<Resource> resources = new ArrayList<Resource>(locationNames.length);

            for (String location : locationNames) {
                location = trimToNull(location);

                if (location != null) {
                    resources.add(loader.getResource(location));
                }
            }

            super.setLocations(resources.toArray(new Resource[resources.size()]));
        }
    }

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
            throws BeansException {
        super.processProperties(beanFactoryToProcess, props);
        this.props = props;
    }

    private String resolveSystemPropertyPlaceholders(String text) {
        StringBuilder buf = new StringBuilder(text);
        int startIndex = buf.indexOf(PLACEHOLDER_PREFIX);

        while( startIndex >= 0) {
            int endIndex = buf.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());

            if (endIndex != -1) {
                String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
                int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();

                try {
                    String value = resolveSystemPropertyPlaceholder(placeholder);

                    if (value != null) {
                        buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), value);
                        nextIndex = startIndex + value.length();
                    } else {
                        log.warn("Could not resolve placeholder '"
                                           + placeholder
                                           + "' in ["
                                           + text
                                           + "] as system property: neither system property nor environment variable found");
                    }
                } catch (Exception ex) {
                    log.error("Could not resolve placeholder '" + placeholder + "' in [" + text
                                       + "] as system property: " + ex);
                }

                startIndex = buf.indexOf(PLACEHOLDER_PREFIX, nextIndex);
            } else {
                startIndex = -1;
            }
        }

        return buf.toString();
    }

    private String resolveSystemPropertyPlaceholder(String placeholder) {
        DefaultablePlaceholder dp = new DefaultablePlaceholder(placeholder);
        String value = System.getProperty(dp.placeholder);

        if (value == null) {
            value = System.getenv(dp.placeholder);
        }

        if (value == null) {
            value = dp.defaultValue;
        }

        return value;
    }

    @Override
    protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
        DefaultablePlaceholder dp = new DefaultablePlaceholder(placeholder);
        String value = super.resolvePlaceholder(dp.placeholder, props, systemPropertiesMode);

        if (value == null) {
            value = dp.defaultValue;
        }

        return trimToEmpty(value);
    }

    /**
     * 获取配置项(返回结果为String)
     * @param key, defaultValue
     * @return
     */
    public String getProperty(String key, String defaultValue) {
        return this.props.getProperty(key, defaultValue);
    }

    public Object setProperty(String key, String value) {
        return this.props.setProperty(key, value);
    }

    /**
     * 获取配置项(返回结果为String)
     * @param key
     * @return
     */
    public String getProperty(String key){
        return StringUtils.trim(props.getProperty(key));
    }

    /**
     * 获取Boolean类型的配置项
     * 注:无该配置荐时,返回false
     * @param key
     * @return
     */
    public boolean getBooleanProperty(String key, boolean defaultValue){
        String value = getProperty(key);
        if(StringUtils.isEmpty(value)) {
            return defaultValue;
        } else {
            return BooleanUtils.toBoolean(value);
        }
    }


    /**
     * 获取Integer类型的配置项
     * 注:无该配置荐时,返回new Integer(0)
     * @param key
     * @return
     */
    public int getIntProperty(String key, int defaultValue){

        String value = getProperty(key);
        if(StringUtils.isEmpty(value)) {
            return defaultValue;
        } else {
            return Integer.parseInt(value);
        }
    }

    /**
     * 获取Long类型的配置项
     * 注:无该配置荐时,返回new Long(0)
     * @param key
     * @return
     */
    public long getLongProperty(String key, long defaultValue){

        String value = getProperty(key);
        if(StringUtils.isEmpty(value)) {
            return defaultValue;
        } else {
            return Long.parseLong(value);
        }
    }


    /**
     * 获取Double类型的配置项
     * 注:无该配置荐时,返回new Double(0)
     * @param key
     * @return
     */
    public double getDoubleProperty(String key, double defaultValue){

        String value = getProperty(key);
        if(StringUtils.isEmpty(value)) {
            return defaultValue;
        } else {
            return Double.parseDouble(value);
        }
    }


    /**
     * 获取Short类型的配置项
     * 注:无该配置荐时,返回new Short(0)
     * @param key
     * @return
     */
    public short getShortProperty(String key, short defaultValue){

        String value = getProperty(key);
        if(StringUtils.isEmpty(value)) {
            return defaultValue;
        } else {
            return Short.parseShort(value);
        }
    }

    /**
     * 获取String []类型的配置项
     * 注:配置项各字符之前通过,分隔 eg param =a,b,c,d
     * @param key
     * @return
     */
    public String[] getStringArrayProperty(String key){

        String value = getProperty(key);
        if(StringUtils.isEmpty(value)) {
            return new String[0];
        } else {
            return value.split(", *");
        }
    }
    private static class DefaultablePlaceholder {

        private final String defaultValue;
        private final String placeholder;

        public DefaultablePlaceholder(String placeholder){
            int commaIndex = placeholder.indexOf(':');

            if (commaIndex >= 0) {
                this.defaultValue = trimToEmpty(placeholder.substring(commaIndex + 1));
                this.placeholder = trimToEmpty(placeholder.substring(0, commaIndex));
            }else{
                this.defaultValue = null;
                this.placeholder = placeholder;
            }

        }
    }

    private String trimToNull(String str) {
        if (str == null) {
            return null;
        }

        String result = str.trim();

        if (result == null || result.length() == 0) {
            return null;
        }

        return result;
    }

    public static String trimToEmpty(String str) {
        if (str == null) {
            return "";
        }

        return str.trim();
    }
}
